package ltesim.xmlParser;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import ltesim.xmlParser.xmlData.XMLDataset;
import ltesim.xmlParser.xmlData.XMLDlschDataset;
import ltesim.xmlParser.xmlData.XMLHIDataset;
import ltesim.xmlParser.xmlData.XMLInitDataset;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

/**
 * @author xirka Main class for parsing custom defined configuration xml files<br/>
 *         Configuration type is defined by according to the first child of aqdb
 *         node, be it init, dlsch, or hi
 */
public class XMLParser {
	private XMLDataset xmlData;
	private String fAbsPath;
	private String fName;
	private int type = 0;
	private String typeName[] = { "N/A", "init", "dlsch", "hi" };

	/**
	 * Create a new XMLParser class<br/>
	 * Parse a file with the absolute path _fAbsPath (ex. C:/init.xml) *
	 * 
	 * @param _fAbsPath
	 * @throws FileNotFoundException
	 */
	public XMLParser(String _fAbsPath) throws FileNotFoundException {
		if (!(new File(_fAbsPath).exists())) {
			throw new FileNotFoundException();
		}

		fAbsPath = _fAbsPath;
		fName = new File(_fAbsPath).getName();
		Document doc = getDocument(_fAbsPath);
		type = getXMLType(doc);
		doParse(doc);
	}

	/**
	 * Reparse the current selected file
	 */
	public void refresh() {
		Document doc = getDocument(fAbsPath);
		doParse(doc);
	}

	/**
	 * Main parsing method, add new config file rules on this function
	 * 
	 * @param doc
	 */
	@SuppressWarnings("unchecked")
	private void doParse(Document doc) {

		String temp = null;
		Node tempNode = null;
		List<Node> nodes;

		switch (type) {
		case 1:
			xmlData = new XMLInitDataset();
			tempNode = doc.selectSingleNode("//aqdb");
			temp = ((Element) tempNode).attributeValue("version");
			xmlData.setData("version", temp);
			temp = ((Element) tempNode).attributeValue("type");
			xmlData.setData("type", temp);

			tempNode = doc.selectSingleNode("//aqdb/init");
			temp = ((Element) tempNode).attributeValue("entry");
			xmlData.setData("entry", temp);

			nodes = doc.selectNodes("//aqdb/init/*");
			// System.out.println("Node.size: "+nodes.size());
			for (Node node : nodes) {
				String key = node.getName();
				String value = ((Element) node).attributeValue("value");
				xmlData.setData(key, value);
				// System.out.println(key+": "+value);
			}
			break;

		case 2:
			xmlData = new XMLDlschDataset();
			tempNode = doc.selectSingleNode("//aqdb");
			temp = ((Element) tempNode).attributeValue("version");
			xmlData.setData("version", temp);
			temp = ((Element) tempNode).attributeValue("type");
			xmlData.setData("type", temp);

			tempNode = doc.selectSingleNode("//aqdb/dlsch");
			temp = ((Element) tempNode).attributeValue("entry");
			xmlData.setData("entry", temp);

			nodes = doc.selectNodes("//aqdb/dlsch/*");
			for (Node node : nodes) {
				String key = node.getName();
				String value = ((Element) node).attributeValue("value");
				xmlData.setData(key, value);
			}
			break;

		case 3:
			xmlData = new XMLHIDataset();
			tempNode = doc.selectSingleNode("//aqdb");
			temp = ((Element) tempNode).attributeValue("version");
			xmlData.setData("version", temp);
			temp = ((Element) tempNode).attributeValue("type");
			xmlData.setData("type", temp);

			tempNode = doc.selectSingleNode("//aqdb/hi");
			temp = ((Element) tempNode).attributeValue("entry");
			xmlData.setData("entry", temp);

			nodes = doc.selectNodes("//aqdb/hi/*");
			for (Node node : nodes) {
				String key = node.getName();
				String value = ((Element) node).attributeValue("value");
				xmlData.setData(key, value);
			}
			break;

		default:
			break;
		}

	}

	/**
	 * Get the type index of the configuration file
	 * 
	 * @return Config type index
	 */
	public int getType() {
		return type;
	}

	/**
	 * Get the type name of the configuration file
	 * 
	 * @return Config type name
	 */
	public String getTypeName() {
		return typeName[type];
	}

	/**
	 * Function for checking the configuration type
	 * 
	 * @param doc
	 * @return index of the configuration file
	 */
	private int getXMLType(Document doc) {
		if (doc.selectNodes("//aqdb/init/*").size() > 0) {
			return 1;
		} else if (doc.selectNodes("//aqdb/dlsch/*").size() > 0) {
			return 2;
		} else if (doc.selectNodes("//aqdb/hi/*").size() > 0) {
			return 3;
		}
		return 0;
	}

	/**
	 * Get the parsed configuration data
	 * 
	 * @return Parsed configuration data
	 */
	public XMLDataset getXMLData() {
		return xmlData;
	}

	/**
	 * Set the parsed configuration data useful for rewriting the file (i.e.
	 * saving)
	 * 
	 * @param _XMLDataset
	 */
	public void setXMLData(XMLDataset _XMLDataset) {
		xmlData = _XMLDataset;
	}

	/**
	 * Write the parsed configuration data to a file in _fabspath
	 * 
	 * @param _fabspath
	 * @throws IOException
	 */
	public void writeXML(String _fabspath) throws IOException {
		assert (type > 0 && type <= typeName.length);
		switch (type) {
		case 1:
			Document initDoc = DocumentHelper.createDocument();

			Element initAqdb = initDoc.addElement("aqdb");
			initAqdb.addAttribute("version", xmlData.sGetData("version"));
			initAqdb.addAttribute("type", xmlData.sGetData("type"));

			Element init = initAqdb.addElement("init");
			init.addAttribute("entry", xmlData.sGetData("entry"));

			String initNodeNames[] = { "sfn", "band", "cell_id",
					"cell_id_group", "cyclic_prefix", "bandwidth", "nrb",
					"phich_duration", "phich_ng", "rs_epre" };

			for (String nodeName : initNodeNames) {
				Element el = init.addElement(nodeName);
				el.addAttribute("value", xmlData.sGetData(nodeName));
			}

			XMLWriter initWriter = new XMLWriter(new FileWriter(_fabspath),
					OutputFormat.createPrettyPrint());
			initWriter.write(initDoc);
			initWriter.close();
			break;
		case 2:
			Document dlschDoc = DocumentHelper.createDocument();

			Element dlschAqdb = dlschDoc.addElement("aqdb");
			dlschAqdb.addAttribute("version", xmlData.sGetData("version"));
			dlschAqdb.addAttribute("type", xmlData.sGetData("type"));

			Element dlsch = dlschAqdb.addElement("dlsch");
			dlsch.addAttribute("entry", xmlData.sGetData("entry"));

			String dlschNodeNames[] = { "rnti", "tti_id", "cqi_req", "dai",
					"harq_process_num", "harq_process_type", "mcch_change",
					"mcs", "n_1a_prb", "n_gap1", "rb_num", "rb_offset",
					"sym_offset", "data_size", "data" };

			for (String nodeName : dlschNodeNames) {
				Element el = dlsch.addElement(nodeName);
				el.addAttribute("value", xmlData.sGetData(nodeName));
			}

			XMLWriter dlschWriter = new XMLWriter(new FileWriter(_fabspath),
					OutputFormat.createPrettyPrint());
			dlschWriter.write(dlschDoc);
			dlschWriter.close();
			break;
		case 3:
			Document hiDoc = DocumentHelper.createDocument();

			Element hiAqdb = hiDoc.addElement("aqdb");
			hiAqdb.addAttribute("version", xmlData.sGetData("version"));
			hiAqdb.addAttribute("type", xmlData.sGetData("type"));

			Element hi = hiAqdb.addElement("hi");
			hi.addAttribute("entry", xmlData.sGetData("entry"));

			String hiNodeNames[] = { "tti_id", "hi", "hi_id", "low_prb_idx",
					"n_dmrs", "n_spead_factor", };

			for (String nodeName : hiNodeNames) {
				Element el = hi.addElement(nodeName);
				el.addAttribute("value", xmlData.sGetData(nodeName));
			}

			XMLWriter hiWriter = new XMLWriter(new FileWriter(_fabspath),
					OutputFormat.createPrettyPrint());
			hiWriter.write(hiDoc);
			hiWriter.close();
			break;
		}

	}

	/**
	 * Write the parsed configuration data to currently opened xmlFile
	 * 
	 * @throws IOException
	 */
	public void writeXML() throws IOException {
		writeXML(fAbsPath);
	}

	/**
	 * Get the absolute path of the currently opened xmlFile
	 * 
	 * @return File absolute path (i.e. C:\\somefile.txt)
	 */
	public String getfAbsPath() {
		return fAbsPath;
	}

	/**
	 * Get the filename of the currently opened xmlFile
	 * 
	 * @return Filename
	 */
	public String getfName() {
		return fName;
	}

	/**
	 * Parse a document from xmlFileName path (see dom4j documentation for more
	 * detail)
	 * 
	 * @param xmlFileName
	 * @return
	 */
	private static Document getDocument(final String xmlFileName) {
		Document document = null;
		SAXReader reader = new SAXReader();
		try {
			document = reader.read(new File(xmlFileName));
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return document;
	}

}
